package com.xyy.saas.payment.adapter.channel;

import com.alibaba.fastjson.JSON;
import com.xyy.saas.payment.adapter.ChannelSettingManager;
import com.xyy.saas.payment.adapter.PaymentAdapter;
import com.xyy.saas.payment.dao.model.WxSettings;
import com.xyy.saas.payment.exception.PaymentException;
import com.xyy.saas.payment.manage.cores.enums.ResultCodeEnum;
import com.xyy.saas.payment.pojo.bo.BusinessPayQueryBo;
import com.xyy.saas.payment.pojo.dto.PayDto;
import com.xyy.saas.payment.pojo.vo.BusinessPayVo;
import com.xyy.saas.payment.util.*;
import com.xyy.util.Base64Util;
import org.apache.commons.lang.StringUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.*;

/**
 * @Author zhangwenxiong
 * @Date 2021/1/13 15:21
 * @Version 1.0
 */
@Component
public class WxCommonAdapter implements PaymentAdapter {
    @Autowired
    private ChannelSettingManager channelSettingManager;

    private static final Logger LOGGER = LoggerFactory.getLogger(WxCommonAdapter.class);

    public String getBill(PayDto payDto) throws PaymentException {
        LOGGER.info("WxCommonAdpter getBill begin params:{}", JSON.toJSONString(payDto));
        Map<String, Object> reqMap = new TreeMap<String, Object>(
                new Comparator<String>() {
                    @Override
                    public int compare(String obj1, String obj2) {
                        // 升序排序
                        return obj1.compareTo(obj2);
                    }
                });
        Map<String, String> wxParam = channelSettingManager.getWXParam(payDto.getBusinessOrderType(), payDto.getPayMode());
        try {

            reqMap.put("mch_id", wxParam.get("mch_id"));
            reqMap.put("appid", wxParam.get("appid"));
            reqMap.put("nonce_str", WxUtil.getNonceStr());
            reqMap.put("bill_date", payDto.getMap().get("billDate").toString());
            reqMap.put("bill_type", "ALL");

            //构建XML
            String reqStr = WxUtil.directMap2Xml(reqMap, wxParam.get("apiKey"));
            String wxUrl = ApolloUtil.getProperty("wx.bill.url");
            String resultXml = HttpClientUtil.xyyHttpPostRequest(wxUrl, reqStr, "application/xml");
            LOGGER.info("WxCommonAdpter getBill return result resultXml:{}", Base64Util.Bytes2Base64(resultXml.getBytes()));
            if (resultXml.contains("error_code")){
                Map<String, String> resultParamsMap = WxUtil.doXMLParse(resultXml);
                LOGGER.error("WxCommonAdpter getBill wechat post error payNo:{}, message:{}", payDto.getPayNo(), JSONUtils.toJSON(resultParamsMap));
                throw new PaymentException(ResultCodeEnum.THIRD_PARAM_ERROR);
            }
            return resultXml;
        }catch (Exception e){
            LOGGER.error("WxCommonAdpter getBill Wechat unified order failed payDto:{}", JSON.toJSON(payDto), e);
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR.getCode(), ResultCodeEnum.CHANNEL_CONNECT_ERROR.getMsg());
        }
    }

    public BusinessPayQueryBo queryRedpack(BusinessPayVo businessPayVo) throws PaymentException {
        BusinessPayQueryBo businessPayQueryBo = new BusinessPayQueryBo();
        SortedMap<String, String> resultMap;
        Map<String, Object> reqMap = new TreeMap<String, Object>(
                new Comparator<String>() {
                    @Override
                    public int compare(String obj1, String obj2) {
                        // 升序排序
                        return obj1.compareTo(obj2);
                    }
                });
        WxSettings wxSettings = channelSettingManager.getWXSettings(businessPayVo.getBusinessOrderType(), businessPayVo.getPayMode());

        if("saas_pack".equals(businessPayVo.getBusinessOrderType()) && !StringUtils.isBlank(businessPayVo.getMchId()) &&
                !Objects.equals(businessPayVo.getMchId(), wxSettings.getMerchantId()))
        {
            WxSettings tempWxSettings = channelSettingManager.getWXSettingByMchId(businessPayVo.getMchId());
            tempWxSettings.setWxAppId(wxSettings.getWxAppId());
            wxSettings = tempWxSettings;
        }

        try {
            reqMap.put("mch_id", wxSettings.getMerchantId());
            reqMap.put("appid", wxSettings.getWxAppId());
            reqMap.put("nonce_str", WxUtil.getNonceStr());
            reqMap.put("mch_billno", businessPayVo.getPayNo());
            reqMap.put("bill_type","MCHT");
            String wxUrl = ApolloUtil.getProperty("wx.query.sendpack.url");

            //构建XML
            String reqStr = WxUtil.directMap2Xml(reqMap, wxSettings.getApiSecret());
            SSLConnectionSocketFactory sslsf = WxUtil.initKeyStoreByPem(wxSettings.getApiCertificate(), wxSettings.getPrivateKey(), wxSettings.getMerchantId());
            CloseableHttpClient httpclient = HttpClients.custom()
                    .setSSLSocketFactory(sslsf)
                    .build();
            HttpPost httpPost = new HttpPost(wxUrl);
            httpPost.setEntity(new StringEntity(reqStr, "UTF-8"));
            CloseableHttpResponse response = httpclient.execute(httpPost);

            String resultStr = "";
            if (response != null && response.getEntity() != null) {
                resultStr = EntityUtils.toString(response.getEntity(), "UTF-8");
                response.close();
            }
            httpclient.close();
            resultMap = WxUtil.doXMLParse(resultStr);
            if ("SUCCESS".equals(resultMap.get("return_code")) && "SUCCESS".equals(resultMap.get("result_code"))) {
                //SENDING:发放中
                //SENT:已发放待领取
                //FAILED：发放失败
                //RECEIVED:已领取
                //RFUND_ING:退款中
                //REFUND:已退款
                businessPayQueryBo.setStatus(resultMap.get("status"));
                if("RECEIVED".equals(resultMap.get("status"))) {
                    businessPayQueryBo.setSuccessTime(new Date());
                }
                if("REFUND".equals(resultMap.get("status"))) {
                    businessPayQueryBo.setSuccessTime(new Date());
                }
                if(StringUtils.isNotEmpty(resultMap.get("reason"))) {
                    businessPayQueryBo.setMsg(resultMap.get("reason"));
                }
                LOGGER.info("WxCommonAdpter#queryRedpack create success businessNo:{},result:{}", businessPayVo.getBusinessNo(), JSON.toJSONString(resultMap));
            } else {
                LOGGER.error("WxCommonAdpter#queryRedpack create failed payDto:{},result:{}", JSON.toJSONString(businessPayVo), JSON.toJSONString(resultMap));
                throw new PaymentException(com.xyy.saas.payment.cores.enums.ResultCodeEnum.CHANNEL_CONNECT_ERROR+":"+resultMap.get("err_code_des"));
            }
        }catch (Exception e){
            LOGGER.error("WxCommonAdpter queryRedpack error payDto:{}", JSON.toJSON(businessPayVo), e);
            throw new PaymentException(ResultCodeEnum.DEFAULT_ERROR);
        }
        return businessPayQueryBo;
    }


    public BusinessPayQueryBo queryBusinessPay(BusinessPayVo businessPayVo) throws PaymentException {
        BusinessPayQueryBo businessPayQueryBo = new BusinessPayQueryBo();
        SortedMap<String, String> resultMap;
        Map<String, Object> reqMap = new TreeMap<String, Object>(
                new Comparator<String>() {
                    @Override
                    public int compare(String obj1, String obj2) {
                        // 升序排序
                        return obj1.compareTo(obj2);
                    }
                });
        WxSettings wxSettings = channelSettingManager.getWXSettings(businessPayVo.getBusinessOrderType(), businessPayVo.getPayMode());

        if("saas_pack".equals(businessPayVo.getBusinessOrderType()) && !StringUtils.isBlank(businessPayVo.getMchId()) &&
                !Objects.equals(businessPayVo.getMchId(), wxSettings.getMerchantId()))
        {
            WxSettings tempWxSettings = channelSettingManager.getWXSettingByMchId(businessPayVo.getMchId());
            tempWxSettings.setWxAppId(wxSettings.getWxAppId());
            wxSettings = tempWxSettings;
        }

        try {
            reqMap.put("mch_id", wxSettings.getMerchantId());
            reqMap.put("appid", wxSettings.getWxAppId());
            reqMap.put("nonce_str", WxUtil.getNonceStr());
            reqMap.put("partner_trade_no", businessPayVo.getPayNo());
            String wxUrl = ApolloUtil.getProperty("wx.query.businessPay.url");

            //构建XML
            String reqStr = WxUtil.directMap2Xml(reqMap, wxSettings.getApiSecret());
            SSLConnectionSocketFactory sslsf = WxUtil.initKeyStoreByPem(wxSettings.getApiCertificate(), wxSettings.getPrivateKey(), wxSettings.getMerchantId());
            CloseableHttpClient httpclient = HttpClients.custom()
                    .setSSLSocketFactory(sslsf)
                    .build();
            HttpPost httpPost = new HttpPost(wxUrl);
            httpPost.setEntity(new StringEntity(reqStr, "UTF-8"));
            CloseableHttpResponse response = httpclient.execute(httpPost);

            String resultStr = "";
            if (response != null && response.getEntity() != null) {
                resultStr = EntityUtils.toString(response.getEntity(), "UTF-8");
                response.close();
            }
            httpclient.close();
            resultMap = WxUtil.doXMLParse(resultStr);
            if ("SUCCESS".equals(resultMap.get("return_code")) && "SUCCESS".equals(resultMap.get("result_code"))) {
                //SUCCESS:发放中
                //FAILED：发放失败
                businessPayQueryBo.setStatus(resultMap.get("status"));
                if("SUCCESS".equals(resultMap.get("status"))) {

                    businessPayQueryBo.setSuccessTime(StringUtils.isNotEmpty(resultMap.get("payment_time"))? DateUtil.parase(resultMap.get("payment_time"), DateUtil.DATEFORMATSECOND):new Date());
                }
                if("FAILED".equals(resultMap.get("status"))) {
                    if(StringUtils.isNotEmpty(resultMap.get("reason"))) {
                        businessPayQueryBo.setMsg(resultMap.get("reason"));
                    }
                }
                LOGGER.info("WxCommonAdpter#queryBusinessPay create success businessNo:{},result:{}", businessPayVo.getBusinessNo(), JSON.toJSONString(resultMap));
            } else {
                LOGGER.error("WxCommonAdpter#queryBusinessPay create failed payDto:{},result:{}", JSON.toJSONString(businessPayVo), JSON.toJSONString(resultMap));
                throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR+":"+resultMap.get("err_code_des"));
            }
        }catch (Exception e){
            LOGGER.error("WxCommonAdpter queryBusinessPay error payDto:{}", JSON.toJSON(businessPayVo), e);
            throw new PaymentException(ResultCodeEnum.DEFAULT_ERROR);
        }
        return businessPayQueryBo;
    }

}
